
package info.abdolahi;

import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Arc;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.PixelMapShader;
import ohos.agp.render.ColorFilter;
import ohos.agp.render.Canvas;
import ohos.agp.render.Texture;
import ohos.agp.render.Shader;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.Element;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.global.systemres.ResourceTable;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;
import ohos.multimodalinput.event.TouchEvent;

import java.io.IOException;

/**
 * Circular music progress bar
 */
public class CircularMusicProgressBar extends Image implements Component.DrawTask, Component.LayoutRefreshedListener, Component.TouchEventListener {

    /**
     * * The constant TAG
     */
    private static final String TAG = "CircularMusicProgressBar";

    /**
     * * The constant SCALE_TYPE
     */
    private static final Image.ScaleMode SCALE_TYPE = ScaleMode.CLIP_CENTER;

    /**
     * * The constant CIRCULARMUSICPROGRESSBAR_BORDER_WIDTH
     */
    private static final String CIRCULARMUSICPROGRESSBAR_BORDER_WIDTH = "border_width";

    /**
     * * The constant CIRCULARMUSICPROGRESSBAR_BORDER_COLOR
     */
    private static final String CIRCULARMUSICPROGRESSBAR_BORDER_COLOR = "border_color";

    /**
     * * The constant CIRCULARMUSICPROGRESSBAR_BORDER_OVERLAY
     */
    private static final String CIRCULARMUSICPROGRESSBAR_BORDER_OVERLAY = "border_overlay";

    /**
     * * The constant CIRCULARMUSICPROGRESSBAR_DRAW_ANTICLOCKWISE
     */
    private static final String CIRCULARMUSICPROGRESSBAR_DRAW_ANTICLOCKWISE = "draw_anticlockwise";

    /**
     * * The constant CIRCULARMUSICPROGRESSBAR_ENABLE_TOUCH
     */
    private static final String CIRCULARMUSICPROGRESSBAR_ENABLE_TOUCH = "enable_touch";

    /**
     * * The constant CIRCULARMUSICPROGRESSBAR_FILL_COLOR
     */
    private static final String CIRCULARMUSICPROGRESSBAR_FILL_COLOR = "fill_color";

    /**
     * * The constant CIRCULARMUSICPROGRESSBAR_PROGRESS_COLOR
     */
    private static final String CIRCULARMUSICPROGRESSBAR_PROGRESS_COLOR = "progress_music_color";

    /**
     * * The constant CIRCULARMUSICPROGRESSBAR_CENTERCIRCLE_DIAMMTERER
     */
    private static final String CIRCULARMUSICPROGRESSBAR_CENTERCIRCLE_DIAMMTERER = "centercircle_diammterer";

    /**
     * * The constant CIRCULARMUSICPROGRESSBAR_PROGRESS_STARTANGLE
     */
    private static final String CIRCULARMUSICPROGRESSBAR_PROGRESS_STARTANGLE = "progress_startAngle";

    /**
     * * The constant COLORDRAWABLE_DIMENSION
     */
    private static final int COLORDRAWABLE_DIMENSION = 2;

    /**
     * * The constant DEFAULT_ANIMATION_TIME
     */
    private static final int DEFAULT_ANIMATION_TIME = 800;

    /**
     * * The constant DEFAULT_BORDER_WIDTH
     */
    private static final int DEFAULT_BORDER_WIDTH = 0;

    /**
     * * The constant DEFAULT_BORDER_COLOR
     */
    private static final int DEFAULT_BORDER_COLOR = Color.BLACK.getValue();

    /**
     * * The constant DEFAULT_FILL_COLOR
     */
    private static final int DEFAULT_FILL_COLOR = Color.TRANSPARENT.getValue();

    /**
     * * The constant DEFAULT_PROGRESS_COLOR
     */
    private static final int DEFAULT_PROGRESS_COLOR = Color.BLUE.getValue();

    /**
     * * The constant DEFAULT_BORDER_OVERLAY
     */
    private static final boolean DEFAULT_BORDER_OVERLAY = false;

    /**
     * * The constant DEFAULT_DRAW_ANTI_CLOCKWISE
     */
    private static final boolean DEFAULT_DRAW_ANTI_CLOCKWISE = false;

    /**
     * * The constant DEFAULT_ENABLE_TOUCH
     */
    private static final boolean DEFAULT_ENABLE_TOUCH = false;

    /**
     * * The constant DEFAULT_INNTER_DAIMMETER_FRACTION
     */
    private static float DEFAULT_INNTER_DAIMMETER_FRACTION = 0.805f;

    /**
     * The constant M shader matrix
     */
    private final Matrix mShaderMatrix = new Matrix();

    /**
     * The constant M bitmap paint
     */
    private final Paint mBitmapPaint = new Paint();

    /**
     * The constant M border paint
     */
    private final Paint mBorderPaint = new Paint();

    /**
     * The constant M fill paint
     */
    private final Paint mFillPaint = new Paint();

    /**
     * The constant M base start angle
     */
    float mBaseStartAngle = 0f;
    /**
     * The constant M drawable rect
     */
    private RectFloat mDrawableRect = new RectFloat();
    /**
     * The constant M border rect
     */
    private RectFloat mBorderRect = new RectFloat();
    /**
     * The constant M border color
     */
    private int mBorderColor = DEFAULT_BORDER_COLOR;
    /**
     * The constant M border width
     */
    private int mBorderWidth = DEFAULT_BORDER_WIDTH;

    /**
     * The constant M fill color
     */
    private int mFillColor = DEFAULT_FILL_COLOR;

    /**
     * The constant M progress color
     */
    private int mProgressColor = DEFAULT_PROGRESS_COLOR;

    /**
     * The constant M bitmap
     */
    private PixelMap mBitmap;

    /**
     * The constant M bitmap shader
     */
    private PixelMapShader mBitmapShader;

    /**
     * The constant M bitmap width
     */
    private int mBitmapWidth;

    /**
     * The constant M bitmap height
     */
    private int mBitmapHeight;

    /**
     * The constant M innr circle diammeter
     */
    private float mInnrCircleDiammeter;

    /**
     * The constant M drawable radius
     */
    private float mDrawableRadius;

    /**
     * The constant M border radius
     */
    private float mBorderRadius;

    /**
     * The constant M progress value
     */
    private float mProgressValue = 0;

    /**
     * The constant M value animator
     */
    private AnimatorValue mValueAnimator;

    /**
     * The constant M color filter
     */
    private ColorFilter mColorFilter;

    /**
     * The constant M ready
     */
    private boolean mReady;

    /**
     * The constant M setup pending
     */
    private boolean mSetupPending;

    /**
     * The constant M border overlay
     */
    private boolean mBorderOverlay;

    /**
     * The constant M draw anti clockwise
     */
    private boolean mDrawAntiClockwise;

    /**
     * The constant M enable touch
     */
    private boolean mEnableTouch;

    /**
     * The constant M disable circular transformation
     */
    private boolean mDisableCircularTransformation;

    /**
     * The constant Animation state
     */
    private boolean animationState = true;

    /**
     * The constant On change listener
     */
    private OnCircularSeekBarChangeListener onChangeListener;

    /**
     * The constant Time interpolator Animator.CurveType
     */
    private static int INTERPOLATOR = Animator.CurveType.LINEAR;

    /**
     * The constant Animator start value
     */
    private float animatorStartValue;

    /**
     * The constant Animator end value
     */
    private float animatorEndValue;

    /**
     * The constant Is draw init
     */
    private boolean isDrawInit = false;

    /**
     * The constant Pixel map holder
     */
    private PixelMapHolder pixelMapHolder;

    /**
     * The constant First down y
     */
    private float firstDownY;

    /**
     * The constant First down x
     */
    private float firstDownX;

    /**
     * The constant M touch slop
     */
    private int mTouchSlop = 20;

    /**
     * The constant Event handler
     */
    private EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner());

    /**
     * The constant Top
     */
    private int top;

    /**
     * The constant Bottom
     */
    private int bottom;

    /**
     * The constant Right
     */
    private int right;

    /**
     * The constant Left
     */
    private int left;

    /**
     * The constant Width
     */
    private int width;

    /**
     * The constant Height
     */
    private int height;

    /**
     * The constant Location on screen x
     */
    private int locationOnScreenX;

    /**
     * The constant Location on screen y
     */
    private int locationOnScreenY;

    /**
     * Circular music progress bar
     *
     * @param context context
     */
    public CircularMusicProgressBar(Context context) {
        super(context);
        initData();
    }

    /**
     * Circular music progress bar
     *
     * @param context context
     * @param attrs   attrs
     */
    public CircularMusicProgressBar(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    /**
     * Circular music progress bar
     *
     * @param context  context
     * @param attrs    attrs
     * @param defStyle def style
     */
    public CircularMusicProgressBar(Context context, AttrSet attrs, String defStyle) {
        super(context, attrs, null);
        mBorderWidth = AttrUtils.getDimensionFromAttr(attrs, CIRCULARMUSICPROGRESSBAR_BORDER_WIDTH, DEFAULT_BORDER_WIDTH);
        mBorderColor = AttrUtils.getColorFromAttr(attrs, CIRCULARMUSICPROGRESSBAR_BORDER_COLOR, DEFAULT_BORDER_COLOR);
        mBorderOverlay = AttrUtils.getBooleanFromAttr(attrs, CIRCULARMUSICPROGRESSBAR_BORDER_OVERLAY, DEFAULT_BORDER_OVERLAY);
        mDrawAntiClockwise = AttrUtils.getBooleanFromAttr(attrs, CIRCULARMUSICPROGRESSBAR_DRAW_ANTICLOCKWISE, DEFAULT_DRAW_ANTI_CLOCKWISE);
        mEnableTouch = AttrUtils.getBooleanFromAttr(attrs, CIRCULARMUSICPROGRESSBAR_ENABLE_TOUCH, DEFAULT_ENABLE_TOUCH);
        mFillColor = AttrUtils.getColorFromAttr(attrs, CIRCULARMUSICPROGRESSBAR_FILL_COLOR, DEFAULT_FILL_COLOR);
        mInnrCircleDiammeter = AttrUtils.getFloatFromAttr(attrs, CIRCULARMUSICPROGRESSBAR_CENTERCIRCLE_DIAMMTERER, DEFAULT_INNTER_DAIMMETER_FRACTION);
        mProgressColor = AttrUtils.getColorFromAttr(attrs, CIRCULARMUSICPROGRESSBAR_PROGRESS_COLOR, DEFAULT_PROGRESS_COLOR);
        mBaseStartAngle = AttrUtils.getFloatFromAttr(attrs, CIRCULARMUSICPROGRESSBAR_PROGRESS_STARTANGLE, DEFAULT_PROGRESS_COLOR);
        initData();
        addDrawTask(this);
        setLayoutRefreshedListener(this);
        invalidate();

    }

    /**
     * Init data
     */
    private void initData() {
        if (mEnableTouch) {
            setTouchEventListener(this);
        }
        mValueAnimator = new AnimatorValue();
        // init animator
        mValueAnimator.setDuration(DEFAULT_ANIMATION_TIME);
        animatorEndValue = mProgressValue;
        animatorStartValue = 0;
        mValueAnimator.setCurveType(INTERPOLATOR);
        mValueAnimator.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                setValueWithNoAnimation(animatorStartValue + value * (animatorEndValue - animatorStartValue));
            }
        });

        super.setScaleMode(SCALE_TYPE);
        mReady = true;

        if (mSetupPending) {
            setup();
            mSetupPending = false;
        }
    }

    /**
     * Get scale mode scale mode
     *
     * @return the scale mode
     */
    @Override
    public ScaleMode getScaleMode() {
        return SCALE_TYPE;
    }

    /**
     * Set scale mode *
     *
     * @param scaleType scale type
     */
    @Override
    public void setScaleMode(ScaleMode scaleType) {
        if (scaleType != SCALE_TYPE) {
            throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
        }
    }

    /**
     * Set on circular bar change listener *
     *
     * @param listener listener
     */
    public void setOnCircularBarChangeListener(OnCircularSeekBarChangeListener listener) {
        this.onChangeListener = listener;
    }

    /**
     * On draw *
     *
     * @param component component
     * @param canvas    canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        LogUtil.info(TAG, " onDraw isinit: " + " isDrawInit: " + isDrawInit);
        if (!isDrawInit) {
            isDrawInit = true;
            super.setPixelMap(null);
            super.setImageElement(null);
        }

        if (mDisableCircularTransformation) {
            super.setPixelMap(mBitmap);
            return;
        }

        if (mBitmap == null) {
            return;
        }


        canvas.save();

        canvas.rotate(mBaseStartAngle, mDrawableRect.getCenter().getPointX(), mDrawableRect.getCenter().getPointY());

        if (mBorderWidth > 0) {
            mBorderPaint.setColor(new Color(mBorderColor));
            Arc arc = new Arc();
            arc.setArc(0, 360, false);
            canvas.drawArc(mBorderRect, arc, mBorderPaint);
        }

        mBorderPaint.setColor(new Color(mProgressColor));

        float sweetAngle = mProgressValue / 100 * 360;
        canvas.drawArc(mBorderRect, new Arc(0, mDrawAntiClockwise ? -sweetAngle : sweetAngle, false), mBorderPaint);

        canvas.restore();

        canvas.drawCircle(mDrawableRect.getCenter().getPointX(), mDrawableRect.getCenter().getPointY(), mDrawableRadius, mBitmapPaint);
        if (mFillColor != Color.TRANSPARENT.getValue()) {
            canvas.drawCircle(mDrawableRect.getCenter().getPointX(), mDrawableRect.getCenter().getPointY(), mDrawableRadius, mFillPaint);
        }
    }

    /**
     * Set value *
     *
     * @param newValue new value
     */
    public void setValue(float newValue) {
        if (animationState) {

            if (mValueAnimator.isRunning()) {
                mValueAnimator.cancel();
            }

            animatorEndValue = newValue;
            animatorStartValue = mProgressValue;

            mValueAnimator.start();
        } else {
            setValueWithNoAnimation(newValue, false);
        }

    }

    /**
     * Set value with no animation *
     *
     * @param newValue new value
     */
    public void setValueWithNoAnimation(float newValue) {
        setValueWithNoAnimation(newValue, false);
    }

    /**
     * Set value with no animation *
     *
     * @param newValue new value
     * @param fromUser from user
     */
    public void setValueWithNoAnimation(float newValue, boolean fromUser) {
        if (onChangeListener != null) {
            onChangeListener.onProgressChanged(this, (int) newValue, fromUser);
        }
        mProgressValue = newValue;
        invalidate();
    }

    /**
     * On refreshed *
     *
     * @param component component
     */
    @Override
    public void onRefreshed(Component component) {
        top = getTop();
        bottom = getBottom();
        right = getRight();
        left = getLeft();
        width = getWidth();
        height = getHeight();
        int[] locationOnScreen = getLocationOnScreen();
        locationOnScreenX = locationOnScreen[0];
        locationOnScreenY = locationOnScreen[1];
        LogUtil.info(TAG, " onRefreshed: "
                + " top: " + top
                + " bottom: " + bottom
                + " left: " + left
                + " right: " + right
                + " width: " + width
                + " height: " + height
                + " locationOnScreenX: " + locationOnScreenX
                + " locationOnScreenY: " + locationOnScreenY);
        setup();
    }

    /**
     * Set padding *
     *
     * @param left   left
     * @param top    top
     * @param right  right
     * @param bottom bottom
     */
    @Override
    public void setPadding(int left, int top, int right, int bottom) {
        super.setPadding(left, top, right, bottom);
        setup();
    }

    /**
     * Set padding relative *
     *
     * @param start  start
     * @param top    top
     * @param end    end
     * @param bottom bottom
     */
    @Override
    public void setPaddingRelative(int start, int top, int end, int bottom) {
        super.setPaddingRelative(start, top, end, bottom);
        setup();
    }

    /**
     * Set progress animation state *
     *
     * @param state state
     */
    public void setProgressAnimationState(boolean state) {
        animationState = state;
    }

    /**
     * Set progress animator interpolator *
     *
     * @param interpolator interpolator
     */
    public void setProgressAnimatorInterpolator(int interpolator) {
        INTERPOLATOR = interpolator;
        initData();
    }

    /**
     * Get border color int
     *
     * @return the int
     */
    public int getBorderColor() {
        return mBorderColor;
    }

    /**
     * Set border color *
     *
     * @param borderColor border color
     */
    public void setBorderColor(int borderColor) {
        if (borderColor == mBorderColor) {
            return;
        }
        mBorderColor = borderColor;
        mBorderPaint.setColor(new Color(mBorderColor));
        invalidate();
    }

    /**
     * Set border progress color *
     *
     * @param borderProgressColor border progress color
     */
    public void setBorderProgressColor(int borderProgressColor) {
        if (borderProgressColor == mProgressColor) {
            return;
        }

        mProgressColor = borderProgressColor;
        invalidate();
    }

    /**
     * Set border color resource *
     *
     * @param borderColorRes border color res
     */
    @Deprecated
    public void setBorderColorResource(int borderColorRes) {
        Element element = null;
        int color = 0;
        try {
            element = getContext().getResourceManager().getElement(borderColorRes);
            color = element.getColor();
        } catch (IOException e) {
            LogUtil.error(AttrUtils.class.getName(), e.getMessage());
        } catch (NotExistException e) {
            LogUtil.error(AttrUtils.class.getName(), e.getMessage());
        } catch (WrongTypeException e) {
            LogUtil.error(AttrUtils.class.getName(), e.getMessage());
        }
        setBorderColor(color);
    }

    /**
     * Get fill color int
     *
     * @return the int
     */
    @Deprecated
    public int getFillColor() {
        return mFillColor;
    }

    /**
     * Set fill color *
     *
     * @param fillColor fill color
     */
    @Deprecated
    public void setFillColor(int fillColor) {
        if (fillColor == mFillColor) {
            return;
        }

        mFillColor = fillColor;
        mFillPaint.setColor(new Color(fillColor));
        invalidate();
    }

    /**
     * Set fill color resource *
     *
     * @param fillColorRes fill color res
     */
    @Deprecated
    public void setFillColorResource(int fillColorRes) {


        int color = 0;
        try {
            Element element = getContext().getResourceManager().getElement(fillColorRes);
            color = element.getColor();
        } catch (IOException e) {
            LogUtil.error(AttrUtils.class.getName(), e.getMessage());
        } catch (NotExistException e) {
            LogUtil.error(AttrUtils.class.getName(), e.getMessage());
        } catch (WrongTypeException e) {
            LogUtil.error(AttrUtils.class.getName(), e.getMessage());
        }
        setFillColor(color);
    }

    /**
     * Get border width int
     *
     * @return the int
     */
    public int getBorderWidth() {
        return mBorderWidth;
    }

    /**
     * Set border width *
     *
     * @param borderWidth border width
     */
    public void setBorderWidth(int borderWidth) {
        if (borderWidth == mBorderWidth) {
            return;
        }

        mBorderWidth = borderWidth;
        setup();
    }

    /**
     * Is border overlay boolean
     *
     * @return the boolean
     */
    public boolean isBorderOverlay() {
        return mBorderOverlay;
    }

    /**
     * Set border overlay *
     *
     * @param borderOverlay border overlay
     */
    public void setBorderOverlay(boolean borderOverlay) {
        if (borderOverlay == mBorderOverlay) {
            return;
        }

        mBorderOverlay = borderOverlay;
        setup();
    }

    /**
     * Is disable circular transformation boolean
     *
     * @return the boolean
     */
    public boolean isDisableCircularTransformation() {
        return mDisableCircularTransformation;
    }

    /**
     * Set disable circular transformation *
     *
     * @param disableCircularTransformation disable circular transformation
     */
    public void setDisableCircularTransformation(boolean disableCircularTransformation) {
        if (mDisableCircularTransformation == disableCircularTransformation) {
            return;
        }

        mDisableCircularTransformation = disableCircularTransformation;
        initializeBitmap();
    }

    /**
     * Set pixel map *
     *
     * @param pixelMap pixel map
     */
    @Override
    public void setPixelMap(PixelMap pixelMap) {
        super.setPixelMap(pixelMap);
        initializeBitmap();
        LogUtil.info(TAG, "setPixelMap");
    }

    /**
     * Set image element *
     *
     * @param element element
     */
    @Override
    public void setImageElement(ohos.agp.components.element.Element element) {
        super.setImageElement(element);
        initializeBitmap();
        LogUtil.info(TAG, "setImageElement");
    }

    /**
     * Set background *
     *
     * @param element element
     */
    @Override
    public void setBackground(ohos.agp.components.element.Element element) {
        super.setBackground(element);
        initializeBitmap();
    }

    /**
     * Set pixel map *
     *
     * @param resId res id
     */
    @Override
    public void setPixelMap(int resId) {
        super.setPixelMap(resId);
        initializeBitmap();
    }

    /**
     * Set pixel map holder *
     *
     * @param pixelMapHolder pixel map holder
     */
    @Override
    public void setPixelMapHolder(PixelMapHolder pixelMapHolder) {
        super.setPixelMapHolder(pixelMapHolder);
    }

    /**
     * Apply color filter
     */
    private void applyColorFilter() {
        if (mBitmapPaint != null) {
            mBitmapPaint.setColorFilter(mColorFilter);
        }
    }

    /**
     * Get bitmap from drawable pixel map
     *
     * @param drawable  drawable
     * @param pixelMap1 pixel map 1
     * @return the pixel map
     */
    private PixelMap getBitmapFromDrawable(ohos.agp.components.element.Element drawable, PixelMap pixelMap1) {
        if (pixelMap1 != null) {
            return pixelMap1;
        }
        if (drawable == null) {
            return null;
        }
        ShapeElement shapeElement = null;
        if (drawable instanceof PixelMapElement) {
            return ((PixelMapElement) drawable).getPixelMap();
        } else if (drawable instanceof ShapeElement) {
            shapeElement = (ShapeElement) drawable;
        } else {
            return null;
        }
        RgbColor[] rgbColors = shapeElement.getRgbColors();

        PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
        initializationOptions.size = new Size(500, 500);
        initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
        PixelMap pixelMap = PixelMap.create( initializationOptions);

        Canvas canvas = new Canvas(new Texture(pixelMap));
        Paint paint = new Paint();
        paint.setColor(new Color(rgbColors[0].asArgbInt()));
        canvas.drawRect(new RectFloat(0, 0, 500, 500), paint);
        return pixelMap;
    }

    /**
     * Initialize bitmap
     */
    private void initializeBitmap() {
        if (mDisableCircularTransformation) {
            mBitmap = null;
        } else {
            ohos.agp.components.element.Element imageElement = getImageElement();
            PixelMap pixelMap = getPixelMap();
            mBitmap = getBitmapFromDrawable(imageElement, pixelMap);
        }
        setup();
    }

    /**
     * Setup
     */
    private void setup() {
        if (!mReady) {
            mSetupPending = true;
            return;
        }

        if (getWidth() == 0 && getHeight() == 0) {
            return;
        }

        if (mBitmap == null) {
            invalidate();
            return;
        }

        mBorderPaint.setStyle(Paint.Style.STROKE_STYLE);
        mBorderPaint.setAntiAlias(true);
        mBorderPaint.setColor(new Color(mBorderColor));
        mBorderPaint.setStrokeWidth(mBorderWidth);
        mBorderPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);

        mFillPaint.setStyle(Paint.Style.FILL_STYLE);
        mFillPaint.setAntiAlias(true);
        mFillPaint.setColor(new Color(mFillColor));

        mBitmapHeight = mBitmap.getImageInfo().size.height;
        mBitmapWidth = mBitmap.getImageInfo().size.width;
        RectFloat rectFloat = calculateBounds();
        mBorderRect = rectFloat;
        mBorderRadius = Math.min((mBorderRect.getHeight() - mBorderWidth) / 2.0f, (mBorderRect.getWidth() - mBorderWidth) / 2.0f);
        mDrawableRect = mBorderRect;
        if (!mBorderOverlay && mBorderWidth > 0) {
            mDrawableRect = insertRectFolat(mDrawableRect, mBorderWidth, mBorderWidth);
        }

        mDrawableRadius = Math.min(mDrawableRect.getHeight() / 2, mDrawableRect.getWidth() / 2);
        if (mInnrCircleDiammeter > 1) {
            mInnrCircleDiammeter = 1;
        }

        mDrawableRadius = mDrawableRadius * mInnrCircleDiammeter;
        updateShaderMatrix();
        invalidate();
    }

    /**
     * Insert rect folat rect float
     *
     * @param mDrawableRect m drawable rect
     * @param mBorderWidth  m border width
     * @param mBorderHeight m border height
     * @return the rect float
     */
    private RectFloat insertRectFolat(RectFloat mDrawableRect, int mBorderWidth, int mBorderHeight) {
        float rectLeft = mDrawableRect.left + mBorderWidth;
        float rectRight = mDrawableRect.right - mBorderWidth;
        float rectTop = mDrawableRect.top + mBorderHeight;
        float rectBottom = mDrawableRect.bottom - mBorderHeight;
        return new RectFloat(rectLeft, rectTop, rectRight, rectBottom);
    }

    /**
     * Calculate bounds rect float
     *
     * @return the rect float
     */
    private RectFloat calculateBounds() {
        int availableWidth = getWidth() - getPaddingLeft() - getPaddingRight();
        int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();
        int sideLength = Math.min(availableWidth, availableHeight);
        float rectLeft = getPaddingLeft() + (availableWidth - sideLength) / 2f;
        float rectTop = getPaddingTop() + (availableHeight - sideLength) / 2f;
        return new RectFloat(rectLeft + getBorderWidth(), rectTop + getBorderWidth(), rectLeft + sideLength - getBorderWidth(), rectTop + sideLength - getBorderWidth());
    }

    /**
     * Update shader matrix
     */
    private void updateShaderMatrix() {
        float scale;
        float dx = 0;
        float dy = 0;
        float[] data1 = mShaderMatrix.getData();
        mShaderMatrix.setMatrix(null);
        float[] data2 = mShaderMatrix.getData();
        if (mBitmapWidth * mDrawableRect.getHeight() > mDrawableRect.getWidth() * mBitmapHeight) {
            scale = mDrawableRect.getHeight() / (float) mBitmapHeight;
            dx = (mDrawableRect.getWidth() - mBitmapWidth * scale) * 0.5f;
        } else {
            scale = mDrawableRect.getWidth() / (float) mBitmapWidth;
            dy = (mDrawableRect.getHeight() - mBitmapHeight * scale) * 0.5f;
        }

        mShaderMatrix.setScale(scale, scale);
        mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
        float[] data = mShaderMatrix.getData();
        pixelMapHolder = new PixelMapHolder(mBitmap);
        mBitmapShader = new PixelMapShader(pixelMapHolder, Shader.TileMode.CLAMP_TILEMODE, Shader.TileMode.CLAMP_TILEMODE);


        mBitmapPaint.setAntiAlias(true);

        mBitmapShader.setShaderMatrix(mShaderMatrix);
        mBitmapPaint.setShader(mBitmapShader, Paint.ShaderType.PIXELMAP_SHADER);
    }

    /**
     * Compute in area boolean
     *
     * @param touchX touch x
     * @param touchY touch y
     * @return the boolean
     */
    private boolean computeInArea(float touchX, float touchY) {
        touchX = touchX - width / 2 - locationOnScreenX;
        touchY = touchY - height / 2 - locationOnScreenY;
        return (Math.sqrt(touchX * touchX + touchY * touchY) <= ((mDrawableRadius / 3) * 2));
    }

    /**
     * Compute and set angle boolean
     *
     * @param touchX touch x
     * @param touchY touch y
     * @return the boolean
     */
    private boolean computeAndSetAngle(float touchX, float touchY) {
        float circleDiameter = mDrawableRadius + mBorderWidth;
        touchX = touchX - width / 2 - locationOnScreenX;
        touchY = touchY - height / 2 - locationOnScreenY;
        double radius = Math.sqrt(touchX * touchX + touchY * touchY);
        if (radius > circleDiameter || radius < ((mDrawableRadius / 3) * 2)) {
            return false;
        }

        int angle;
        if (mDrawAntiClockwise) {
            angle = (int) ((180.0 * Math.atan2(touchX, touchY) / Math.PI) - mBaseStartAngle);
        } else {
            angle = (int) ((180.0 * Math.atan2(touchY, touchX) / Math.PI) - mBaseStartAngle);
        }
        angle = ((angle > 0) ? angle : 360 + angle);
        float intoPercent = angle * 100 / 360;
        setValueWithNoAnimation(intoPercent, true);
        return true;
    }


    /**
     * On touch event boolean
     *
     * @param component  component
     * @param touchEvent touch event
     * @return the boolean
     */
    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        boolean isDispatch = false;
        switch (touchEvent.getAction()) {
            case TouchEvent.PRIMARY_POINT_DOWN:
                isDispatch = handleActionDown(touchEvent);
                break;
            case TouchEvent.POINT_MOVE:
                isDispatch = handleActionMove(touchEvent);
                break;
            case TouchEvent.PRIMARY_POINT_UP:
                handleActionUp(touchEvent);
                break;
            case TouchEvent.CANCEL:
                break;
        }

        return isDispatch;
    }

    /**
     * Set clicked listener *
     *
     * @param listener listener
     */
    @Override
    public void setClickedListener(ClickedListener listener) {

    }

    /**
     * Handle action down boolean
     *
     * @param event event
     * @return the boolean
     */
    private boolean handleActionDown(TouchEvent event) {
        firstDownX = event.getPointerPosition(0).getX();
        firstDownY = event.getPointerPosition(0).getY();
        eventHandler.postTask(new Runnable() {
            @Override
            public void run() {
                if (onChangeListener != null) {
                    onChangeListener.onLongPress(CircularMusicProgressBar.this);
                }
            }
        }, 500);
        if (computeInArea(event.getPointerPosition(0).getX(), event.getPointerPosition(0).getY())) {
            if (onChangeListener != null) {
                onChangeListener.onClick(CircularMusicProgressBar.this);
            }
            return true;
        } else if (computeAndSetAngle(event.getPointerPosition(0).getX(), event.getPointerPosition(0).getY())) {
            return true;
        }
        return false;
    }

    /**
     * Handle action move boolean
     *
     * @param event event
     * @return the boolean
     */
    private boolean handleActionMove(TouchEvent event) {
        if (Math.abs(event.getPointerPosition(0).getY() - firstDownY) > mTouchSlop
                && Math.abs(event.getPointerPosition(0).getX() - firstDownX) > mTouchSlop) {
            computeAndSetAngle(event.getPointerPosition(0).getX(), event.getPointerPosition(0).getY());
            eventHandler.removeAllEvent();
            return true;
        }
        return false;
    }

    /**
     * Handle action up *
     *
     * @param event event
     */
    private void handleActionUp(TouchEvent event) {
        eventHandler.removeAllEvent();
    }
}
